/**
 * simple view object
 * created by David hua
 * @2016/7/31
 */
var vo = {};
(function(vo){
	var eventRegExp = new RegExp('[\\w\\W]{1,}(?=[:])');
	var plugins = {};
	vo.newObj = function(params){
		if(params.el == null || params.el == '' ){
			throw new Error("the parameter of 'el' is empty");
		}
		var id = params.el;
		if(id.substring(0,1) == '#'){
			this.el = document.getElementById(id.substring(1));
		}else{
			throw new Error('illegal el');
		}
		if(this.el == null){
			if(params.html != null && params.html != ''){
				this.el = document.createElement("div");
				this.el.id = id.substring(1);
				this.el.innerHTML = params.html;
			}else{
				throw new Error("id '"+id.substring(1)+"' doesn't have any html content!");
			}
			if(params.parent == null || params.parent == ''){
				this.parent = document;
			}else if(params.parent.substring(0,1) == '#'){
				var parentId = params.parent;
				this.parent = document.getElementById(parentId.substring(1));
				if(this.parent == null){
					throw new Error("couldn't find the parent element whose id '"+id.substring(1)+"'");
				}
				this.parent.appendChild(this.el);
			}else{
				throw new Error("illegal parent");
			}
		}
		if(params.datas != null){
			this.setDatas(params.datas);
			this.flush();
		}
		if(params.methods != null&& params.methods instanceof Object){
			this.setMethods(params.methods);
		}
	};
	vo.newObj.prototype = {
		mapper : function(key,value){//map the K/V to view(elements)
			var props = getElementsByAttr_r(this.el,'name',key);
			if(null != props){
				for(var j = 0;j<props.length;j++){
					this.setValue(props[j],value);
				}
			}
		},
		bindMethod : function(){//bind the methods to view(elements)
			//else scan the elements which have attribute named 'vo-m'
			var elements = getElementsByAttr_r(this.el,'vo-m', '[\\w\\W]{1,}');
			for(var i = 0;i<elements.length;i++){
				var attr = elements[i].getAttribute('vo-m');//event:method
				var type = eventRegExp.exec(attr);//type like 'click','blur'....
				var name = attr.substring(type[0].length+1);
				//if the el is dynamic created,bind the method to its parent elements
				if(typeof(this.methods[name]) != 'undefined'){
					if(this.parent != null){
						vo.events.add(this.el.id,type,name,this.methods[name], this.parent);
					}else{//bind the method to the el element
						vo.events.add(this.el.id,type,name,this.methods[name],this.el);
					}
				}
			}
		},
		setValue : function(element,value){//set value according to tagName,such as 'input','div'..
			if(element.tagName.toLowerCase() == 'input'){
				element.value = value;
			}else{
				element.innerHTML = value;
			}
		},
		flush : function(){
			for(var i in this.datas){
				this.mapper(i,this.datas[i]);
			}
		},
		modify : function(key,value){//modify the K/V
			vo.invoke('modify');
			this.datas[key] = value;
			this.mapper(key,value);
		},
		del : function(key){//delete K/V (doesn't affect the HTML code)
			vo.invoke('del');
			delete this.datas[key];
			this.mapper(key,'');
		},
		get : function(key){//get V from K
			vo.invoke('get');
			return this.datas[key];
		},
		add : function(key,value){// add K/V
			vo.invoke('add');
			this.datas[key] = value;
			this.mapper(key,value);
		},
		setDatas : function(datas_new){
			this.datas = datas_new;
		},
		getDatas : function(){
			return this.datas;
		},
		setMethods : function(methods){
			if(typeof(this.methods) == 'undefined'){
				this.methods = {};
			}
			for(var name in  methods){
				this.methods[name] = methods[name].bind(this,this.datas);
			}
			this.bindMethod(this.el);
		},
	};
	var events = function(){
		this.typesEVs = {};//三级分类存储events 如:type类型 -> id号 -> name名称 
		this.elementIds = [];
	};
	events.prototype = {
		add:function(id,type,name,ev,element){
			var typeEVs = null;
			if(typeof(typeEVs = this.typesEVs[type])=='undefined'){
				this.typesEVs[type] = {};
				typeEVs = this.typesEVs[type];
			};
			var EVs = null;
			if(typeof(EVs = typeEVs[id]) =='undefined'){
				typeEVs[id] = {};
				EVs = typeEVs[id];
			}
			EVs[name] = ev;
			if(typeof(element)!='undefined'&&
					typeof(element.id)!='undefined'&&this.elementIds.indexOf(element.id)<0){
				this.elementIds.push(element.id);
				if(type == "click"){
					element.onclick = function(){
						if(this.trigger()){
							stopBubble();
						}
					}.bind(this);
				}
			}
		},
		trigger:function(id,type,name,element){
			var source = typeof(element)=='undefined'?target():element;
			if(source != document){
				var attr = source.getAttribute("vo-m");
			}
			var parent = source;
			while((typeof(id) == 'undefined'||id == null)&&parent!=null){//往上查找父节点的id
				id = parent.getAttribute("id");
				parent =  parent.parentNode;
			}
			if(typeof(attr)!='undefined'&&attr!=null){
				if(typeof(type)=='undefined'){
					type = eventRegExp.exec(attr);
				}//type like 'click','blur'....
				if(typeof(name) == 'undefined'){
					name = attr.substring(type[0].length+1);
				}
			}
			var typeEVs = this.typesEVs[type];
			var EVs = null;
			if(typeof(typeEVs)!='undefined'
					&&typeof(EVs = typeEVs[id])!='undefined'
						&&typeof(EVs[name])!='undefined'){
				vo.invoke(name);
				EVs[name]();
				return true;
			}
			return false;
		},
		remove:function(id,type,name){
			delete this.typesEVs[type][id][name];
		}
	};
	vo.events = new events();
	vo.plugin = function(method,plugin){//add plug-in methods
		plugins[method] = plugin;
	};
	vo.invoke = function(method){//invoke plug-in methods
		var plugin = plugins[method];
		if(typeof(plugin)!='undefined'){
			plugin();
		}
	};
	vo.remove = function(method){//delete plug-in methods
		delete plugins[method];
	};
	function getElementsByAttr_r(root,attr,regExp){
		var reg = new RegExp(regExp);
		var elements = root.getElementsByTagName('*');
		var returns = [];
		if(elements != null && elements.length > 0){
			for(var i = 0;i<elements.length;i++){
				var attribute = elements[i].getAttribute(attr);
				if(attribute!=null&&reg.test(attribute)){
					returns.push(elements[i]);
				}
			}
		}
		return returns;
	}
	function target(){
		var event = window.event||
			arguments.callee.caller.arguments[0];
		var target = event.target||event.srcElement;
		return target;
	};
	function stopBubble(){
		var event = arguments.callee.caller.arguments[0];
		if(event){
			event.stopPropagation();
		}else if(window.event){
			window.event.cancelBubble = true;
		}
	}
})(vo);